package com.orange.core.security.repository;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.orange.core.security.detail.UserDetail;
import com.orange.core.security.token.WxAuthenticationToken;
import com.orange.core.security.token.WxPersistenceToken;
import com.orange.core.util.Constants;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;

import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

@RequiredArgsConstructor
public class WxSecurityContextRepository extends DelegatingContextRepository {

    private final ObjectMapper objectMapper;
    private final StringRedisTemplate redisTemplate;

    @SneakyThrows
    @Override
    public SecurityContext loadContext(SecurityContext securityContext, String token) {
        if (StrUtil.isNotBlank(token)) {
            String tokenStr = redisTemplate.boundValueOps(Constants.USER_TOKEN_REDIS_KEY + token).get();
            if (StrUtil.isNotBlank(tokenStr)) {
                WxPersistenceToken authenticationPersistenceToken = objectMapper.readValue(tokenStr, WxPersistenceToken.class);
                securityContext.setAuthentication(authenticationPersistenceToken.toAuthenticationToken());
            }
        }
        return securityContext;
    }

    @SneakyThrows
    @Override
    protected void saveContext(Authentication authentication, HttpServletResponse response) {
        if (authentication instanceof WxAuthenticationToken authenticationToken && authentication.getPrincipal() instanceof UserDetail userDetail) {
            String token = userDetail.getToken();
            BoundValueOperations<String, String> valueOps = redisTemplate.boundValueOps(Constants.USER_TOKEN_REDIS_KEY + token);
            valueOps.set(objectMapper.writeValueAsString(WxPersistenceToken.of(authenticationToken)));
            valueOps.expire(Constants.WX_MINI_MAX_INACTIVE_INTERVAL, TimeUnit.SECONDS);

            BoundSetOperations<String, String> ops = redisTemplate.boundSetOps(Constants.USER_TOKEN_REDIS_KEY + authentication.getName());
            ops.add(token);
            ops.expire(Constants.WX_MINI_MAX_INACTIVE_INTERVAL, TimeUnit.SECONDS);
        }
    }
}
